# Python 异常处理

# 为什么需要异常处理
# 1. 避免程序崩溃：程序运行过程中，可能会出现各种各样的异常，如输入错误、文件操作失败、网络连接失败等等。
# 2. 方便后续排查问题：程序运行过程中，如果出现异常，可以记录异常信息，方便后续排查问题。
# 3. 提供友好的错误提示：程序运行过程中，如果出现异常，可以向用户提供友好的错误提示，帮助用户快速定位问题。
# 4. 资源清理：确保文件、网络连接资源正确释放


# 常见的异常类型
# 1. ZeroDivisionError：除数为零
# 2. NameError：未定义的变量
# 3. TypeError：数据类型错误
# 4. IndexError：下标越界
# 5. ValueError：数据值错误
# 6. KeyError：字典中不存在的键
# 7. AttributeError：对象没有这个属性
# 8. ImportError：导入模块失败
# 9. IOError：文件操作失败
# 10. SyntaxError：语法错误
# 11. IndentationError：缩进错误
# 12. SystemExit：程序终止
# 13. KeyboardInterrupt：用户中断程序


# 异常语法基础
try:
    # 可能发生异常的代码
    x = 1 / 0
except ZeroDivisionError:
    # 捕获到 ZeroDivisionError 异常后执行的代码
    print("数学是体育老师教的吗?")
except:
    # 捕获到其他异常后执行的代码
    print("专业写bug")
else:
    # 没有异常发生时执行的代码
    print("Python大神")
finally:
    # 无论是否发生异常都会执行的代码(如资源清理)
    print("不干也得干")


# 抛出异常
# 1. 手动抛出异常，使用 raise 关键字
# raise Exception("手动抛出异常")
x = 0
try:
    if x == 0:
        raise ZeroDivisionError("傻B吧?")
except ZeroDivisionError as e:                  # 捕获到 ZeroDivisionError 异常并将异常对象赋值给变量 e
    # 捕获到 ZeroDivisionError 异常后打印异常信息
    print(e)


# 2. 自动抛出异常，使用 assert 关键字
# assert x!= 0, "x 不能等于 0"
x = 0
try:
    # 使用断言 assert 关键字自动抛出异常，断言条件为false时抛出 AssertionError 异常，后面紧跟异常信息
    assert x != 0, "x 不能等于 0"
except AssertionError as e:                     # 捕获到 AssertionError 异常并将异常对象赋值给变量 e
    # 捕获到 AssertionError 异常后打印异常信息
    print(e)



# 案例：文件操作异常处理
try:
    with open("D:\PyCharmProjects\python-demo\output.txt", "r") as f:
        # 可能发生异常的代码
        # 读取文件内容
        content = f.read()
        print("文件内容为：", content)
        # 关闭文件
        f.close()
except (FileNotFoundError, PermissionError) as e:
    # 捕获到 FileNotFoundError 异常后执行的代码
    print("读写文件失败")
except IOError:
    # 捕获到 IOError 异常后执行的代码
    print("文件操作失败")
else:
    # 没有异常发生时执行的代码
    print(content)
finally:
    # 无论是否发生异常都会执行的代码(如资源清理)
    print("读取文件完成")




# 自定义异常
# 自定义一个无效的数组异常类，继承自 Exception 类
class InvalidNumberException(Exception):
    """无效的数字异常"""

    def __init__(self, value):                     # 构造函数，接收异常信息
        self.value = value
        super().__init__(f"无效的数字：{value}")     # 调用父类构造函数，传入异常信息


    def __str__(self):
        return "无效的数字: {}".format(self.value)

# 定义一个设置数字的函数，接收一个数字参数，如果不是有效的数字，则抛出 InvalidNumberException 异常
def set_number(num):
    """设置数字"""
    if num < 0 or num > 100:
        raise InvalidNumberException(num)      # 手动抛出异常
    print("设置数字为：", num)

# 测试自定义异常
try:
    set_number(101)
except InvalidNumberException as e:        # 捕获到 InvalidNumberException 异常并将异常对象赋值给变量 e
    print(e)